package algoritmi;

import java.util.ArrayList;

/**
 * 
 * @author Maja Kabiljo
 * 
 */
abstract class AlgoritamImpl implements Algoritam {
	/**
	 * Koliko mesta ima u kesu.
	 */
	protected int velicinaKesa;
	/**
	 * Koliko mesta ima u memoriji.
	 */
	protected int velicinaMemorije;
	/**
	 * Sta se nalazi na svakom mestu u kesu; -1 ako je prazno.
	 */
	protected int[] kes;

	/**
	 * Odakle da citamo podatke.
	 */
	protected Ucitavanje ucitavanje;

	@Override
	public void postaviVelicinuKesa(int velicinaKesa) {
		this.velicinaKesa = velicinaKesa;
	}
	@Override
	public void postaviVelicinuGlavneMemorije(int velicinaMemorije) {
		this.velicinaMemorije = velicinaMemorije;
	}

	@Override
	public void setIzvorPodataka(Ucitavanje ucitavanje) {
		this.ucitavanje = ucitavanje;
	}

	/**
	 * @return - Vraca memorijske reference za koje treba odraditi algoritam.
	 */
	protected int[] nabaviPodatke() {
		return ucitavanje.ucitaj();
	}

	@Override
	public ArrayList<InformacijeOJednomKoraku> getKoraci() {
		kes = new int[velicinaKesa];
		for (int i = 0; i < velicinaKesa; i++)
			kes[i] = -1;
		return odradiAlgoritam(nabaviPodatke());
	}

	/**
	 * Priprema za pocetak algoritma.
	 * 
	 * @param koraci
	 *            - koje su memorijske reference.
	 */
	protected abstract void start(int[] koraci);
	/**
	 * @return - Vraca trenutno stanje svih elemenata u kesu.
	 */
	protected abstract int[] getVrednosti();
	/**
	 * Obavlja sta treba kada je doslo do pogodka.
	 * 
	 * @param korak
	 *            - korak u kom je doslo do pogodka.
	 * @param pozicijaUKesu
	 *            - na kojoj poziciji u kesu se dogodio pogodak.
	 */
	protected abstract void pogodak(int korak, int pozicijaUKesu);
	/**
	 * Obavlja sta treba kada je doslo do promasaja.
	 * 
	 * @param korak
	 *            - korak u kom je doslo do promasaja.
	 * @return - Vraca na koje mesto u kesu ce se smestiti trazeni podatak.
	 */
	protected abstract int promasaj(int korak);

	/**
	 * Izvrsava algoritam.
	 * 
	 * @param koraci
	 *            - memorijske reference za koje ga treba izvrsiti.
	 * @return - Vraca, korak po korak, kako je funkcionisao algoritam.
	 */
	protected ArrayList<InformacijeOJednomKoraku> odradiAlgoritam(int[] koraci) {
		start(koraci);
		ArrayList<InformacijeOJednomKoraku> ret = new ArrayList<InformacijeOJednomKoraku>();
		int brojPogodaka = 0;
		for (int i = 0; i < koraci.length; i++) {
			int pozicija = pozicijaUKesu(koraci[i]);
			if (pozicija != -1) {
				pogodak(i, pozicija);
				ret.add(new InformacijeOJednomKoraku(koraci[i], pozicija, true, Utils
						.pretvoriNizU0doN(getVrednosti()), ++brojPogodaka));
			} else {
				int pokazivac = promasaj(i);
				kes[pokazivac] = koraci[i];
				ret.add(new InformacijeOJednomKoraku(koraci[i], pokazivac, false, Utils
						.pretvoriNizU0doN(getVrednosti()), brojPogodaka));
			}
		}
		return ret;
	}

	/**
	 * @param stranica
	 * @return vraca indeks na kom se u kesu nalazi <code>stranica</code>,
	 *         odnosno -1 ukoliko nije u kesu
	 */
	protected int pozicijaUKesu(int stranica) {
		for (int i = 0; i < velicinaKesa; i++)
			if (kes[i] == stranica)
				return i;
		return -1;
	}
}
